﻿//
// Copyright (c) 2024 BPer
//
#include <bperrtc/BPerPlayTrack.h>
#include <bperutil/sys/BPerLog.h>
#include <bperavutil/video/BPerNalu.h>

#include <bperrtc/BPerRtcRtcp.h>
#include <bperrtc/BPerPlayTrackH.h>

#include <math.h>

void bPer_create_playTrack(BPerRtcContext *context, BPerPlayTrack *track, BPerBool is_audio) {
	if (track == NULL)
		return;
	track->uid = context->streamConfig->uid;
	track->isAudio = is_audio;
	track->rtp_queue = (BPerReceiveNackBuffer*) bPer_calloc(1,sizeof(BPerReceiveNackBuffer));
	track->nack_receiver = (BPerRtpRecvNack*) bPer_calloc(1,sizeof(BPerRtpRecvNack));
	if (is_audio) {
		track->ssrc = context->audioTrack.ssrc;
		bPer_create_nackBuffer(track->rtp_queue, 100);
		bPer_create_recvnack(track->nack_receiver, track->rtp_queue, 100 * 2 / 3);
	} else {
        if(context->videoTracks.vsize>0) track->ssrc=context->videoTracks.payload[0].ssrc;
		bPer_create_nackBuffer(track->rtp_queue, 1000);
		bPer_create_recvnack(track->nack_receiver, track->rtp_queue, 1000 * 2 / 3);
	}
	track->last_sender_report_rtp_time = 0;
	track->last_sender_report_sys_time = 0;

	track->last_seq = 0;
	track->enableAudioBuffer=context->avinfo->rtc.enableAudioBuffer;
	track->sortBuffer=NULL;
	bPer_memset(&track->audioFrame, 0, sizeof(BPerFrame));
}

void bPer_destroy_playTrack(BPerPlayTrack *track) {
	if (track == NULL)
		return;

	bPer_destroy_nackBuffer(track->rtp_queue);
	bPer_free(track->rtp_queue);
	bPer_destroy_recvnack(track->nack_receiver);
	bPer_free(track->nack_receiver);
}

int32_t bPer_playtrack_has_ssrc(BPerRtcContext *context, BPerPlayTrack *track,
		uint32_t ssrc) {

	if (track->isAudio)
		return bPer_has_ssrc(&context->audioTrack, ssrc) ? 1 : 0;
	else{
		for(int i=0;i<context->videoTracks.vsize;i++){
			if( bPer_has_ssrc(&context->videoTracks.payload[i], ssrc)) return 1;
		}
	}
	return 0;
}

void bPer_playtrack_update_rtt(BPerPlayTrack *track, int32_t rtt) {
	bPer_recvnack_update_rtt(track->nack_receiver, rtt);

}

void bPer_playtrack_update_send_report_time(BPerPlayTrack *track, BPerNtp *ntp,
		uint32_t rtp_time) {

	bPer_memcpy(&track->last_sender_report_ntp, ntp, sizeof(BPerNtp));
	track->last_sender_report_rtp_time = rtp_time;

	track->last_sender_report_sys_time = bPer_update_system_time();
}

int32_t bPer_playtrack_send_rtcp_rr(BPerRtcContext *context,BPerBool isAudio,BPerPlayTrack *track) {
	int32_t err = BPer_Ok;

	uint32_t ssrc = track->ssrc;
	const uint64_t last_time = track->last_sender_report_sys_time;


	if ((err = bPer_send_rtcp_rr(context, isAudio,ssrc, track->rtp_queue, last_time,
			&track->last_sender_report_ntp)) != BPer_Ok) {
		return bPer_error_wrap(err, "ssrc=%u, last_time=%lld", ssrc, last_time);
	}

	return err;
}

int32_t bPer_playtrack_send_rtcp_xr_rrtr(BPerRtcContext *context,
		BPerPlayTrack *track) {
	int32_t err = BPer_Ok;

	if ((err = bPer_send_rtcp_xr_rrtr(context, track->ssrc)) != BPer_Ok) {
		return bPer_error_wrap(err, "ssrc=%u", track->ssrc);
	}

	return err;
}


int32_t bPer_playtrack_on_nack(BPerRtcContext *context, BPerPlayTrack *track,BPerRtpPacket *pkt) {
	int32_t err = BPer_Ok;

	uint16_t seq = pkt->header.sequence;
	if (track->nack_receiver->queue.vsize > 0) {
		BPerRtpNackInfo *nack_info = bPer_recvnack_find(track->nack_receiver,seq);
		if (nack_info) {
			//receive lost seq
			bPer_trace("\nreceive nack seq=%hu", seq);
			bPer_recvnack_remove(track->nack_receiver, seq);
			return err;
		}
	}
	// insert check nack
	uint16_t nack_first = 0, nack_last = 0;

	bPer_nackbuffer_update2(track->rtp_queue, seq, &nack_first, &nack_last);
	if (bPer_rtp_seq_distance(nack_first, nack_last) > 0) {
		bPer_trace("\nnack check: lost current seq=%u, nack range [%u, %u]", seq,nack_first, nack_last);
		bPer_recvnack_insert(track->nack_receiver, nack_first, nack_last);
		bPer_recvnack_check_queue_size(track->nack_receiver);

	}

//insert nack
	bPer_nackbuffer_set(track->rtp_queue, seq, pkt->header.sequence);
	if (!bPer_recvnack_isEmpty(track->nack_receiver)) {
		uint32_t timeout_nacks = 0;
		bPer_playtrack_do_check_send_nacks(context, track, &timeout_nacks);
	}

	return err;
}

int32_t bPer_playtrack_do_check_send_nacks(BPerRtcContext *context,BPerPlayTrack *track, uint32_t *timeout_nacks) {
	uint32_t sent_nacks = 0;
	return bPer_check_send_nacks(context, track->nack_receiver, track->ssrc,&sent_nacks, timeout_nacks);

}

void bPer_playtrack_receiveVideo(BPerRtcContext *context,BPerFrame *videoFrame) {
    if(videoFrame==NULL||videoFrame->payload==NULL||videoFrame->nb>BPER_VIDEO_ENCODE_BUFFER_LEN) return;
    uint8_t first=videoFrame->payload[0];
	uint8_t *temp = videoFrame->payload+1;
    int32_t videoLen = videoFrame->nb-1;

    videoFrame->dts=videoFrame->pts;

    videoFrame->payload = temp ;
    videoFrame->nb = videoLen;

    if(first == 0x27){
        videoFrame->frametype = BPER_FrameType_P;
        if(bPer_hasH264Pframe(videoFrame->payload)) goto sendevideo;
    }

    if (first == 0x17) {

#if BPer_Rtp_I_Withmeta
    	if(context->avinfo->video.videoDecHwType==BPer_Hw_Soft){
    		videoFrame->frametype =BPER_FrameType_I;
    		goto sendevideo;
    	}
#endif

    	BPerH264NaluData nalu;
    	bPer_parseH264Nalu(videoFrame,&nalu);

    	if(nalu.spsppsPos>-1&&nalu.keyframePos>-1){
    		uint8_t meta[BPer_Nalu_SpsppsBufferLen] = { 0 };
    		videoFrame->payload=meta;
    		if(bPer_getH264SpsppseNalu(videoFrame,temp+nalu.spsppsPos)!=BPer_Ok){
    			bPer_error("get h264 nalue fail");
    			return;
    		}
    		videoFrame->frametype = BPER_FrameType_Spspps;

    		if (context &&context->streamConfig->recvCallback.receiveVideo){
    			context->streamConfig->recvCallback.receiveVideo(context->streamConfig->recvCallback.context,videoFrame);
    		}
    	}

    	if(nalu.keyframePos>-1){
    		videoFrame->payload = temp + nalu.keyframePos;
    		videoFrame->nb = videoLen - nalu.keyframePos;
    		videoFrame->frametype =BPER_FrameType_I;
    		goto sendevideo;
    	}
    }

    if( first == 0x2c){
        videoFrame->frametype = BPER_FrameType_P;
        goto sendevideo;
        return;
    }

    if ( first == 0x1c ) {

    	BPerH264NaluData nalu;
    	bPer_parseH265Nalu(videoFrame,&nalu);

    	if(nalu.spsppsPos>-1&&nalu.keyframePos>-1){
    		uint8_t meta[BPer_Nalu_SpsppsBufferLen] = { 0 };
    		videoFrame->payload=meta;
    		if(bPer_getH265SpsppseNalu(videoFrame,temp + nalu.spsppsPos)!=BPer_Ok){
    			bPer_error("get h265 nalue fail");
    			return;
    		}
    		videoFrame->frametype = BPER_FrameType_Spspps;

    		if (context && context->streamConfig->recvCallback.receiveVideo)
    			context->streamConfig->recvCallback.receiveVideo(context->streamConfig->recvCallback.context,videoFrame);
    	}
    	if(nalu.keyframePos>-1){
    		videoFrame->payload = temp + nalu.keyframePos;
    		videoFrame->nb = videoLen - nalu.keyframePos;
    		videoFrame->frametype = BPER_FrameType_I;
    		goto sendevideo;

    	}

    }

    return;
    sendevideo:
		videoFrame->uid=context->streamConfig->uid;
		if (context &&context->streamConfig->recvCallback.receiveVideo){
			context->streamConfig->recvCallback.receiveVideo(context->streamConfig->recvCallback.context,videoFrame);
		}
		return;

}
